import xml.dom.minidom
import database as db
import datetime

DATETIMEFORMAT = "%Y-%m-%d %H:%M:%S"

def backup(database, filename):
    # Helper functions
    def addField(node, id, value):
        fieldNode = doc.createElement("field")
        fieldNode.setAttribute("id", id)
        fieldNode.appendChild(doc.createTextNode(value))
        node.appendChild(fieldNode)

    def addLink(node, id, value):
        linkNode = doc.createElement("link")
        linkNode.setAttribute("id", id)
        linkNode.appendChild(doc.createTextNode(value))
        node.appendChild(linkNode)

    # Initialize XML document
    impl = xml.dom.minidom.getDOMImplementation()
    doc = impl.createDocument("", "database", None)
    root = doc.documentElement

    # Add stars
    database.log.info("Backing up stars")
    stars = db.Star.query.all()
    for star in stars:
        starNode = doc.createElement("star")
        addField(starNode, "objname", str(star.objname))
        addField(starNode, "ra", str(float(star.ra)))
        addField(starNode, "dec", str(float(star.dec)))
        root.appendChild(starNode)
    
    # Add catalogs
    database.log.info("Backing up catalogs")
    catalogs = db.Catalog.query.all()
    for cat in catalogs:
        catNode = doc.createElement("catalog")
        addField(catNode, "name", str(cat.name))
        for star in cat.stars:
            addLink(catNode, "star", str(star.objname))
        root.appendChild(catNode)

    # Add star magnitudes
    database.log.info("Backing up star magnitudes")
    magnitudes = db.StarMagnitude.query.all()
    for mag in magnitudes:
        magNode = doc.createElement("star-magnitude")
        addLink(magNode, "star", str(mag.star.objname))
        addLink(magNode, "source", str(mag.source.name))
        addField(magNode, "filter", str(mag.filter))
        addField(magNode, "magnitude", str(float(mag.magnitude)))
        root.appendChild(magNode)

    # Add exposures
    database.log.info("Backing up exposures")
    exps = db.Exposure.query.all()
    for exp in exps:
        expNode = doc.createElement("exposure")
        addField(expNode, "filename", str(exp.filename))
        addField(expNode, "ra", str(float(exp.ra)))
        addField(expNode, "dec", str(float(exp.dec)))
        addField(expNode, "alt", str(float(exp.alt)))
        addField(expNode, "az", str(float(exp.az)))
        addField(expNode, "lst", str(float(exp.lst)))
        addField(expNode, "latitude", str(float(exp.latitude)))
        addField(expNode, "longitude", str(float(exp.longitude)))
        addField(expNode, "obsdate", str(exp.obsdate))
        addField(expNode, "imagetype", str(exp.imagetype))
        addField(expNode, "exptime", str(float(exp.exptime)))
        addField(expNode, "filter", str(exp.filter))
        addField(expNode, "observer", str(exp.observer))
        root.appendChild(expNode)

    # Add exposure notes
    database.log.info("Backing up exposure notes")
    notes = db.ExposureTask.query.all()
    for note in notes:
        noteNode = doc.createElement("exposure-note")
        addLink(noteNode, "exposure", str(note.exposure.filename))
        addField(noteNode, "name", str(note.name))
        addField(noteNode, "detail", str(note.detail))
        addField(noteNode, "date", str(note.date))
        root.appendChild(noteNode)

    # Add exposure stars
    database.log.info("Backing up exposure stars")
    expstars = db.ExposureStar.query.all()
    for star in expstars:
        starNode = doc.createElement("exposure-star")
        addLink(starNode, "star", str(star.star.objname))
        addLink(starNode, "exposure", str(star.exposure.filename))
        addField(starNode, "photra", str(star.photra))
        addField(starNode, "photdec", str(star.photdec))
        addField(starNode, "alt", str(star.alt))
        addField(starNode, "az", str(star.az))
        addField(starNode, "fwhm", str(star.fwhm))
        addField(starNode, "flux", str(star.flux))
        addField(starNode, "magnitude", str(star.magnitude))
        addField(starNode, "airmass", str(star.airmass))
        root.appendChild(starNode)

    # Add extinction data
    database.log.info("Backing up extinction data")
    exts = db.Extinction.query.all()
    for ext in exts:
        extNode = doc.createElement("extinction")
        addField(extNode, "starttime", str(ext.starttime))
        addField(extNode, "endtime", str(ext.endtime))
        addField(extNode, "refband", str(ext.refband))
        addField(extNode, "otherbands", str(ext.otherbands))
        addField(extNode, "coefficient1", str(float(ext.coefficient1)))
        addField(extNode, "coefficient2", str(float(ext.coefficient2)))

    # Write to file
    database.log.info("Writing backup to file '%s'" % (filename))
    file = open(filename, 'w')
    doc.writexml(file)
    file.flush()
    file.close()

    # Clean up
    doc.unlink()

def restore(database, filename):
    # Helper functions
    def getField(node, id):
        nodes = node.getElementsByTagName("field")
        for node in nodes:
            if node.getAttribute("id") == id and isinstance(node.firstChild, xml.dom.minidom.Text):
                return node.firstChild.data
        return None

    def getLink(node, id):
        nodes = node.getElementsByTagName("link")
        for node in nodes:
            if node.getAttribute("id") == id and isinstance(node.firstChild, xml.dom.minidom.Text):
                return node.firstChild.data
        return None

    def getLinks(node, id):
        nodes = node.getElementsByTagName("link")
        links = []
        for node in nodes:
            if node.getAttribute("id") == id and isinstance(node.firstChild, xml.dom.minidom.Text):
                links.append(node.firstChild.data)
        return links

    # Read from file
    database.log.info("Reading backup from file '%s'" % (filename))
    doc = xml.dom.minidom.parse(filename)

    # Read stars
    starNodes = doc.getElementsByTagName("star")
    database.log.info("Reading stars from backup (%d)" % (len(starNodes)))
    for starNode in starNodes:
        if not database.containsStar(getField(starNode, "objname")):
            db.Star(objname = getField(starNode, "objname"),
                    ra = float(getField(starNode, "ra")),
                    dec = float(getField(starNode, "dec")))
    if len(starNodes) > 0: db.session.commit()

    # Read catalogs
    catNodes = doc.getElementsByTagName("catalog")
    database.log.info("Reading catalogs from backup (%d)" % (len(catNodes)))
    for catNode in catNodes:
        if not database.containsCatalog(getField(catNode, "name")):
            cat = db.Catalog(name = getField(catNode, "name"))
            stars = getLinks(catNode, "star")
            for star in stars:
                star = database._getStar(star)
                cat.stars.append(star)
    if len(catNodes) > 0: db.session.commit()

    # Read star magnitudes
    magNodes = doc.getElementsByTagName("star-magnitude")
    database.log.info("Reading magnitudes from backup (%d)" % (len(magNodes)))
    for magNode in magNodes:
        star = database._getStar(getLink(magNode, "star"))
        source = database._getCatalog(getLink(magNode, "source"))
        filter = getField(magNode, "filter")
        if db.StarMagnitude.query.filter_by(star = star).filter_by(source = source).filter_by(filter = filter).count() == 0:
            db.StarMagnitude(star = star, source = source, filter = filter,
                             magnitude = float(getField(magNode, "magnitude")))
    if len(magNodes) > 0: db.session.commit()

    # Read exposures
    expNodes = doc.getElementsByTagName("exposure")
    database.log.info("Reading exposures from backup (%d)" % (len(expNodes)))
    for expNode in expNodes:
        if not database.containsExposure(getField(expNode, "filename")):
            db.Exposure(filename = getField(expNode, "filename"),
                        ra = float(getField(expNode, "ra")),
                        dec = float(getField(expNode, "dec")),
                        alt = float(getField(expNode, "alt")),
                        az = float(getField(expNode, "az")),
                        lst = float(getField(expNode, "lst")),
                        latitude = float(getField(expNode, "latitude")),
                        longitude = float(getField(expNode, "longitude")),
                        obsdate = datetime.datetime.strptime(getField(expNode, "obsdate").split('.')[0], DATETIMEFORMAT),
                        exptime = float(getField(expNode, "exptime")),
                        imagetype = getField(expNode, "imagetype"),
                        filter = getField(expNode, "filter"),
                        observer = getField(expNode, "observer"))
    if len(expNodes) > 0: db.session.commit()

    # Read exposure notes
    noteNodes = doc.getElementsByTagName("exposure-note")
    database.log.info("Reading exposure notes from backup (%d)" % (len(noteNodes)))
    for noteNode in noteNodes:
        db.ExposureTask(exposure = database._getExposure(getLink(noteNode, "exposure")),
                        name = getField(noteNode, "name"),
                        detail = getField(noteNode, "detail"),
                        date = datetime.datetime.strptime(getField(noteNode, "date").split('.')[0], DATETIMEFORMAT))
    if len(noteNodes) > 0: db.session.commit()

    # Read exposure stars
    starNodes = doc.getElementsByTagName("exposure-star")
    database.log.info("Reading exposure stars from backup (%d)" % (len(starNodes)))
    for starNode in starNodes:
        db.ExposureStar(exposure = database._getExposure(getLink(starNode, "exposure")),
                        star = database._getStar(getLink(starNode, "star")),
                        photra = float(getField(starNode, "photra")),
                        photdec = float(getField(starNode, "photdec")),
                        alt = float(getField(starNode, "alt")),
                        az = float(getField(starNode, "az")),
                        fwhm = float(getField(starNode, "fwhm")),
                        flux = float(getField(starNode, "flux")),
                        magnitude = float(getField(starNode, "magnitude")),
                        airmass = float(getField(starNode, "airmass")))
    if len(starNodes) > 0: db.session.commit()

    # Read extinction data
    extNodes = doc.getElementsByTagName("extinction")
    database.log.info("Reading extinction data from backup (%d)" % (len(extNodes)))
    for extNode in extNodes:
        db.Extinction(starttime = datetime.datetime.strptime(getField(extNode, "starttime").split('.')[0], DATETIMEFORMAT),
                      endtime = datetime.datetime.strptime(getField(extNode, "endtime").split('.')[0], DATETIMEFORMAT),
                      refband = getField(extNode, "refband"),
                      otherbands = getField(extNode, "otherbands"),
                      coefficient1 = float(getField(extNode, "coefficient1")),
                      coefficient2 = float(getField(extNode, "coefficient2")))
    if len(extNodes) > 0: db.session.commit()